home *** CD-ROM | disk | FTP | other *** search
/ Aminet 48 / Aminet 48 (2002)(GTI - Schatztruhe)[!][Apr 2002].iso / Aminet / text / edit / vim60src.lha / Vim / vim60 / src / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-09-10  |  51.8 KB  |  2,327 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *                GUI/Motif support by Robert Webb
  5.  *
  6.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  7.  * Do ":help credits" in Vim to see a list of people who contributed.
  8.  * See README.txt for an overview of the Vim source code.
  9.  */
  10.  
  11. /*
  12.  * Code for menus.  Used for the GUI and 'wildmenu'.
  13.  */
  14.  
  15. #include "vim.h"
  16.  
  17. #if defined(FEAT_MENU) || defined(PROTO)
  18.  
  19. #define MENUDEPTH   10        /* maximum depth of menus */
  20.  
  21. #ifdef FEAT_GUI_W32
  22. static int add_menu_path __ARGS((char_u *, vimmenu_T *, int *, char_u *, int));
  23. #else
  24. static int add_menu_path __ARGS((char_u *, vimmenu_T *, int *, char_u *));
  25. #endif
  26. static int menu_nable_recurse __ARGS((vimmenu_T *menu, char_u *name, int modes, int enable));
  27. static int remove_menu __ARGS((vimmenu_T **, char_u *, int, int silent));
  28. static void free_menu __ARGS((vimmenu_T **menup));
  29. static void free_menu_string __ARGS((vimmenu_T *, int));
  30. static int show_menus __ARGS((char_u *, int));
  31. static void show_menus_recursive __ARGS((vimmenu_T *, int, int));
  32. static int menu_name_equal __ARGS((char_u *name, vimmenu_T *menu));
  33. static int menu_namecmp __ARGS((char_u *name, char_u *mname));
  34. static int get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
  35. static char_u *popup_mode_name __ARGS((char_u *name, int idx));
  36. static char_u *menu_text __ARGS((char_u *text, int *mnemonic, char_u **actext));
  37. #ifdef FEAT_GUI
  38. static int get_menu_mode __ARGS((void));
  39. static void gui_update_menus_recurse __ARGS((vimmenu_T *, int));
  40. #endif
  41.  
  42. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  43. static void gui_create_tearoffs_recurse __ARGS((vimmenu_T *menu, const char_u *pname, int *pri_tab, int pri_idx));
  44. static void gui_add_tearoff __ARGS((char_u *tearpath, int *pri_tab, int pri_idx));
  45. static void gui_destroy_tearoffs_recurse __ARGS((vimmenu_T *menu));
  46. static int s_tearoffs = FALSE;
  47. #endif
  48.  
  49. static int menu_is_hidden __ARGS((char_u *name));
  50. #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
  51. static int menu_is_tearoff __ARGS((char_u *name));
  52. #endif
  53.  
  54. #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
  55. static char_u *menu_skip_part __ARGS((char_u *p));
  56. #endif
  57. #ifdef FEAT_MULTI_LANG
  58. static char_u *menutrans_lookup __ARGS((char_u *name, int len));
  59. #endif
  60.  
  61. /* The character for each menu mode */
  62. static char_u    menu_mode_chars[] = {'n', 'v', 'o', 'i', 'c', 't'};
  63.  
  64. static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
  65. static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
  66. static char_u e_nomenu[] = N_("E329: No menu of that name");
  67.  
  68. #ifdef FEAT_TOOLBAR
  69. static const char *toolbar_names[] =
  70. {
  71.     /*  0 */ "New", "Open", "Save", "Undo", "Redo",
  72.     /*  5 */ "Cut", "Copy", "Paste", "Print", "Help",
  73.     /* 10 */ "Find", "SaveAll", "SaveSesn", "NewSesn", "LoadSesn",
  74.     /* 15 */ "RunScript", "Replace", "WinClose", "WinMax", "WinMin",
  75.     /* 20 */ "WinSplit", "Shell", "FindPrev", "FindNext", "FindHelp",
  76.     /* 25 */ "Make", "TagJump", "RunCtags", "WinVSplit", "WinMaxWidth",
  77.     /* 30 */ "WinMinWidth", "Exit"
  78. };
  79. # define TOOLBAR_NAME_COUNT (sizeof(toolbar_names) / sizeof(char *))
  80. #endif
  81.  
  82. /*
  83.  * Do the :menu command and relatives.
  84.  */
  85.     void
  86. ex_menu(eap)
  87.     exarg_T    *eap;            /* Ex command arguments */
  88. {
  89.     char_u    *menu_path;
  90.     int        modes;
  91.     char_u    *map_to;
  92.     int        noremap;
  93.     int        silent = FALSE;
  94.     int        unmenu;
  95.     char_u    *map_buf;
  96.     char_u    *arg;
  97.     char_u    *p;
  98.     int        i;
  99. #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
  100.     int        old_menu_height;
  101. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  102.     int        old_toolbar_height;
  103. # endif
  104. #endif
  105.     int        pri_tab[MENUDEPTH + 1];
  106.     int        enable = MAYBE;        /* TRUE for "menu enable", FALSE for "menu
  107.                      * disable */
  108. #ifdef FEAT_MULTI_LANG
  109.     char_u    *tofree = NULL;
  110.     char_u    *new_cmd;
  111. #endif
  112. #ifdef FEAT_TOOLBAR
  113.     char_u    *icon = NULL;
  114. #endif
  115.     vimmenu_T    menuarg;
  116.  
  117.     modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
  118.     arg = eap->arg;
  119.  
  120.     for (;;)
  121.     {
  122.     if (STRNCMP(arg, "<script>", 8) == 0)
  123.     {
  124.         noremap = REMAP_SCRIPT;
  125.         arg = skipwhite(arg + 8);
  126.         continue;
  127.     }
  128.     if (STRNCMP(arg, "<silent>", 8) == 0)
  129.     {
  130.         silent = TRUE;
  131.         arg = skipwhite(arg + 8);
  132.         continue;
  133.     }
  134.     break;
  135.     }
  136.  
  137.  
  138.     /* Locate an optional "icon=filename" argument. */
  139.     if (STRNCMP(arg, "icon=", 5) == 0)
  140.     {
  141.     arg += 5;
  142. #ifdef FEAT_TOOLBAR
  143.     icon = arg;
  144. #endif
  145.     while (*arg != NUL && *arg != ' ')
  146.     {
  147.         if (*arg == '\\')
  148.         mch_memmove(arg, arg + 1, STRLEN(arg));
  149. #ifdef FEAT_MBYTE
  150.         if (has_mbyte)
  151.         arg += (*mb_ptr2len_check)(arg);
  152.         else
  153. #endif
  154.         ++arg;
  155.     }
  156.     if (*arg != NUL)
  157.     {
  158.         *arg++ = NUL;
  159.         arg = skipwhite(arg);
  160.     }
  161.     }
  162.  
  163.     /*
  164.      * Fill in the priority table.
  165.      */
  166.     for (p = arg; *p; ++p)
  167.     if (!isdigit(*p) && *p != '.')
  168.         break;
  169.     if (vim_iswhite(*p))
  170.     {
  171.     for (i = 0; i < MENUDEPTH && !vim_iswhite(*arg); ++i)
  172.     {
  173.         pri_tab[i] = getdigits(&arg);
  174.         if (pri_tab[i] == 0)
  175.         pri_tab[i] = 500;
  176.         if (*arg == '.')
  177.         ++arg;
  178.     }
  179.     arg = skipwhite(arg);
  180.     }
  181.     else if (eap->addr_count && eap->line2 != 0)
  182.     {
  183.     pri_tab[0] = eap->line2;
  184.     i = 1;
  185.     }
  186.     else
  187.     i = 0;
  188.     while (i < MENUDEPTH)
  189.     pri_tab[i++] = 500;
  190.     pri_tab[MENUDEPTH] = -1;        /* mark end of the table */
  191.  
  192.     /*
  193.      * Check for "disable" or "enable" argument.
  194.      */
  195.     if (STRNCMP(arg, "enable", 6) == 0 && vim_iswhite(arg[6]))
  196.     {
  197.     enable = TRUE;
  198.     arg = skipwhite(arg + 6);
  199.     }
  200.     else if (STRNCMP(arg, "disable", 7) == 0 && vim_iswhite(arg[7]))
  201.     {
  202.     enable = FALSE;
  203.     arg = skipwhite(arg + 7);
  204.     }
  205.  
  206.     /*
  207.      * If there is no argument, display all menus.
  208.      */
  209.     if (*arg == NUL)
  210.     {
  211.     show_menus(arg, modes);
  212.     return;
  213.     }
  214.  
  215. #ifdef FEAT_TOOLBAR
  216.     /*
  217.      * Need to get the toolbar icon index before doing the translation.
  218.      */
  219.     menuarg.iconidx = -1;
  220.     menuarg.icon_builtin = FALSE;
  221.     if (menu_is_toolbar(arg))
  222.     {
  223.     menu_path = menu_skip_part(arg);
  224.     if (*menu_path == '.')
  225.     {
  226.         p = menu_skip_part(++menu_path);
  227.         if (STRNCMP(menu_path, "BuiltIn", 7) == 0)
  228.         {
  229.         if (skipdigits(menu_path + 7) == p)
  230.         {
  231.             menuarg.iconidx = atoi((char *)menu_path + 7);
  232.             if (menuarg.iconidx >= TOOLBAR_NAME_COUNT)
  233.             menuarg.iconidx = -1;
  234.             else
  235.             menuarg.icon_builtin = TRUE;
  236.         }
  237.         }
  238.         else
  239.         {
  240.         for (i = 0; i < TOOLBAR_NAME_COUNT; ++i)
  241.             if (STRNCMP(toolbar_names[i], menu_path, p - menu_path)
  242.                                      == 0)
  243.             {
  244.             menuarg.iconidx = i;
  245.             break;
  246.             }
  247.         }
  248.     }
  249.     }
  250. #endif
  251.  
  252. #ifdef FEAT_MULTI_LANG
  253.     /*
  254.      * Translate menu names as specified with ":menutrans" commands.
  255.      */
  256.     menu_path = arg;
  257.     while (*menu_path)
  258.     {
  259.     /* find the end of one part and check if it should be translated */
  260.     p = menu_skip_part(menu_path);
  261.     map_to = menutrans_lookup(menu_path, (int)(p - menu_path));
  262.     if (map_to != NULL)
  263.     {
  264.         /* found a match: replace with the translated part */
  265.         i = (int)STRLEN(map_to);
  266.         new_cmd = alloc((unsigned)STRLEN(arg) + i + 1);
  267.         if (new_cmd == NULL)
  268.         break;
  269.         mch_memmove(new_cmd, arg, menu_path - arg);
  270.         mch_memmove(new_cmd + (menu_path - arg), map_to, (size_t)i);
  271.         STRCPY(new_cmd + (menu_path - arg) + i, p);
  272.         p = new_cmd + (menu_path - arg) + i;
  273.         vim_free(tofree);
  274.         tofree = new_cmd;
  275.         arg = new_cmd;
  276.     }
  277.     if (*p != '.')
  278.         break;
  279.     menu_path = p + 1;
  280.     }
  281. #endif
  282.  
  283.     /*
  284.      * Isolate the menu name.
  285.      * Skip the menu name, and translate <Tab> into a real TAB.
  286.      */
  287.     menu_path = arg;
  288.     if (*menu_path == '.')
  289.     {
  290.     EMSG2(_(e_invarg2), menu_path);
  291.     goto theend;
  292.     }
  293.  
  294.     while (*arg && !vim_iswhite(*arg))
  295.     {
  296.     if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL)
  297.         arg++;
  298.     else if (STRNICMP(arg, "<TAB>", 5) == 0)
  299.     {
  300.         *arg = TAB;
  301.         mch_memmove(arg + 1, arg + 5, STRLEN(arg + 4));
  302.     }
  303.     arg++;
  304.     }
  305.     if (*arg != NUL)
  306.     *arg++ = NUL;
  307.     arg = skipwhite(arg);
  308.     map_to = arg;
  309.  
  310.     /*
  311.      * If there is only a menu name, display menus with that name.
  312.      */
  313.     if (*map_to == NUL && !unmenu && enable == MAYBE)
  314.     {
  315.     show_menus(menu_path, modes);
  316.     goto theend;
  317.     }
  318.     else if (*map_to != NUL && (unmenu || enable != MAYBE))
  319.     {
  320.     EMSG(_(e_trailing));
  321.     goto theend;
  322.     }
  323. #if defined(FEAT_GUI) && !(defined(FEAT_GUI_GTK) || defined(FEAT_GUI_PHOTON))
  324.     old_menu_height = gui.menu_height;
  325. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  326.     old_toolbar_height = gui.toolbar_height;
  327. # endif
  328. #endif
  329.  
  330.     if (enable != MAYBE)
  331.     {
  332.     /*
  333.      * Change sensitivity of the menu.
  334.      * Careful: menu_nable_recurse() changes menu_path.
  335.      */
  336.     if (STRCMP(menu_path, "*") == 0)    /* meaning: do all menus */
  337.         menu_path = (char_u *)"";
  338.     menu_nable_recurse(root_menu, menu_path, modes, enable);
  339.     }
  340.     else if (unmenu)
  341.     {
  342.     /*
  343.      * Delete menu(s).
  344.      */
  345.     if (STRCMP(menu_path, "*") == 0)    /* meaning: remove all menus */
  346.         menu_path = (char_u *)"";
  347.  
  348.     /*
  349.      * For the PopUp menu, remove a menu for each mode separately.
  350.      */
  351.     if (menu_is_popup(menu_path))
  352.     {
  353.         for (i = 0; i < MENU_INDEX_TIP; ++i)
  354.         if (modes & (1 << i))
  355.         {
  356.             p = popup_mode_name(menu_path, i);
  357.             if (p != NULL)
  358.             {
  359.             remove_menu(&root_menu, p, MENU_ALL_MODES, TRUE);
  360.             vim_free(p);
  361.             }
  362.         }
  363.     }
  364.  
  365.     /* Careful: remove_menu() changes menu_path */
  366.     remove_menu(&root_menu, menu_path, modes, FALSE);
  367.     }
  368.     else
  369.     {
  370.     /*
  371.      * Add menu(s).
  372.      * Replace special key codes.
  373.      */
  374.     map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE);
  375.     menuarg.modes = modes;
  376. #ifdef FEAT_TOOLBAR
  377.     menuarg.iconfile = icon;
  378. #endif
  379.     menuarg.noremap[0] = noremap;
  380.     menuarg.silent[0] = silent;
  381.     add_menu_path(menu_path, &menuarg, pri_tab, map_to
  382. #ifdef FEAT_GUI_W32
  383.         , TRUE
  384. #endif
  385.         );
  386.  
  387.     /*
  388.      * For the PopUp menu, add a menu for each mode separately.
  389.      */
  390.     if (menu_is_popup(menu_path))
  391.     {
  392.         for (i = 0; i < MENU_INDEX_TIP; ++i)
  393.         if (modes & (1 << i))
  394.         {
  395.             p = popup_mode_name(menu_path, i);
  396.             if (p != NULL)
  397.             {
  398.             /* Include all modes, to make ":amenu" work */
  399.             menuarg.modes = modes;
  400. #ifdef FEAT_TOOLBAR
  401.             menuarg.iconfile = NULL;
  402.             menuarg.iconidx = -1;
  403.             menuarg.icon_builtin = FALSE;
  404. #endif
  405.             add_menu_path(p, &menuarg, pri_tab, map_to
  406. #ifdef FEAT_GUI_W32
  407.                 , TRUE
  408. #endif
  409.                      );
  410.             vim_free(p);
  411.             }
  412.         }
  413.     }
  414.  
  415.     vim_free(map_buf);
  416.     }
  417.  
  418. #if defined(FEAT_GUI) && !defined(FEAT_GUI_GTK)
  419.     /* If the menubar height changed, resize the window */
  420.     if (gui.in_use
  421.         && (gui.menu_height != old_menu_height
  422. # if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_W16)
  423.         || gui.toolbar_height != old_toolbar_height
  424. # endif
  425.         ))
  426.     gui_set_shellsize(FALSE, FALSE);
  427. #endif
  428.  
  429. theend:
  430. #ifdef FEAT_MULTI_LANG
  431.     vim_free(tofree);
  432. #else
  433.     ;
  434. #endif
  435. }
  436.  
  437. /*
  438.  * Add the menu with the given name to the menu hierarchy
  439.  */
  440.     static int
  441. add_menu_path(menu_path, menuarg, pri_tab, call_data
  442. #ifdef FEAT_GUI_W32
  443.     , addtearoff
  444. #endif
  445.     )
  446.     char_u    *menu_path;
  447.     vimmenu_T    *menuarg;    /* passes modes, iconfile, iconidx,
  448.                    icon_builtin, silent[0], noremap[0] */
  449.     int        *pri_tab;
  450.     char_u    *call_data;
  451. #ifdef FEAT_GUI_W32
  452.     int        addtearoff;    /* may add tearoff item */
  453. #endif
  454. {
  455.     char_u    *path_name;
  456.     int        modes = menuarg->modes;
  457.     vimmenu_T    **menup;
  458.     vimmenu_T    *menu = NULL;
  459.     vimmenu_T    *parent;
  460.     vimmenu_T    **lower_pri;
  461.     char_u    *p;
  462.     char_u    *name;
  463.     char_u    *dname;
  464.     char_u    *next_name;
  465.     int        i;
  466.     int        c;
  467. #ifdef FEAT_GUI
  468.     int        idx;
  469.     int        new_idx;
  470. #endif
  471.     int        pri_idx = 0;
  472.     int        old_modes = 0;
  473.     int        amenu;
  474.  
  475.     /* Make a copy so we can stuff around with it, since it could be const */
  476.     path_name = vim_strsave(menu_path);
  477.     if (path_name == NULL)
  478.     return FAIL;
  479.     menup = &root_menu;
  480.     parent = NULL;
  481.     name = path_name;
  482.     while (*name)
  483.     {
  484.     /* Get name of this element in the menu hierarchy, and the simplified
  485.      * name (without mnemonic and accelerator text). */
  486.     next_name = menu_name_skip(name);
  487.     dname = menu_text(name, NULL, NULL);
  488.  
  489.     /* See if it's already there */
  490.     lower_pri = menup;
  491. #ifdef FEAT_GUI
  492.     idx = 0;
  493.     new_idx = 0;
  494. #endif
  495.     menu = *menup;
  496.     while (menu != NULL)
  497.     {
  498.         if (menu_name_equal(name, menu) || menu_name_equal(dname, menu))
  499.         {
  500.         if (*next_name == NUL && menu->children != NULL)
  501.         {
  502.             if (!sys_menu)
  503.             EMSG(_("E330: Menu path must not lead to a sub-menu"));
  504.             goto erret;
  505.         }
  506.         if (*next_name != NUL && menu->children == NULL
  507. #ifdef FEAT_GUI_W32
  508.             && addtearoff
  509. #endif
  510.             )
  511.         {
  512.             if (!sys_menu)
  513.             EMSG(_(e_notsubmenu));
  514.             goto erret;
  515.         }
  516.         break;
  517.         }
  518.         menup = &menu->next;
  519.  
  520.         /* Count menus, to find where this one needs to be inserted.
  521.          * Ignore menus that are not in the menubar (PopUp and Toolbar) */
  522.         if (parent != NULL || menu_is_menubar(menu->name))
  523.         {
  524. #ifdef FEAT_GUI
  525.         ++idx;
  526. #endif
  527.         if (menu->priority <= pri_tab[pri_idx])
  528.         {
  529.             lower_pri = menup;
  530. #ifdef FEAT_GUI
  531.             new_idx = idx;
  532. #endif
  533.         }
  534.         }
  535.         menu = menu->next;
  536.     }
  537.  
  538.     if (menu == NULL)
  539.     {
  540.         if (*next_name == NUL && parent == NULL)
  541.         {
  542.         EMSG(_("E331: Must not add menu items directly to menu bar"));
  543.         goto erret;
  544.         }
  545.  
  546.         if (menu_is_separator(dname) && *next_name != NUL)
  547.         {
  548.         EMSG(_("E332: Separator cannot be part of a menu path"));
  549.         goto erret;
  550.         }
  551.  
  552.         /* Not already there, so lets add it */
  553.         menu = (vimmenu_T *)alloc_clear((unsigned)sizeof(vimmenu_T));
  554.         if (menu == NULL)
  555.         goto erret;
  556.  
  557.         menu->modes = modes;
  558.         menu->enabled = MENU_ALL_MODES;
  559.         menu->name = vim_strsave(name);
  560.         /* separate mnemonic and accelerator text from actual menu name */
  561.         menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
  562.         menu->priority = pri_tab[pri_idx];
  563.         menu->parent = parent;
  564. #ifdef FEAT_GUI_MOTIF
  565.         menu->sensitive = TRUE;        /* the default */
  566. #endif
  567. #ifdef FEAT_BEVAL
  568.         menu->tip = NULL;
  569. #endif
  570. #ifdef FEAT_GUI_ATHENA
  571.         menu->image = None;            /* X-Windows definition for NULL*/
  572. #endif
  573.  
  574.         /*
  575.          * Add after menu that has lower priority.
  576.          */
  577.         menu->next = *lower_pri;
  578.         *lower_pri = menu;
  579.  
  580.         old_modes = 0;
  581.  
  582. #ifdef FEAT_TOOLBAR
  583.         menu->iconidx = menuarg->iconidx;
  584.         menu->icon_builtin = menuarg->icon_builtin;
  585.         if (*next_name == NUL && menuarg->iconfile != NULL)
  586.         menu->iconfile = vim_strsave(menuarg->iconfile);
  587. #endif
  588. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  589.         /* the tearoff item must be present in the modes of each item. */
  590.         if (parent != NULL && menu_is_tearoff(parent->children->dname))
  591.         parent->children->modes |= modes;
  592. #endif
  593.     }
  594.     else
  595.     {
  596.         old_modes = menu->modes;
  597.  
  598.         /*
  599.          * If this menu option was previously only available in other
  600.          * modes, then make sure it's available for this one now
  601.          * Also enable a menu when it's created or changed.
  602.          */
  603. #ifdef FEAT_GUI_W32
  604.         /* If adding a tearbar (addtearoff == FALSE) don't update modes */
  605.         if (addtearoff)
  606. #endif
  607.         {
  608.         menu->modes |= modes;
  609.         menu->enabled |= modes;
  610.         }
  611.     }
  612.  
  613. #ifdef FEAT_GUI
  614.     /*
  615.      * Add the menu item when it's used in one of the modes, but not when
  616.      * only a tooltip is defined.
  617.      */
  618.     if ((old_modes & MENU_ALL_MODES) == 0
  619.         && (menu->modes & MENU_ALL_MODES) != 0)
  620.     {
  621.         if (gui.in_use)  /* Otherwise it will be added when GUI starts */
  622.         {
  623.         if (*next_name == NUL)
  624.         {
  625.             /* Real menu item, not sub-menu */
  626.             gui_mch_add_menu_item(menu, new_idx);
  627.  
  628.             /* Want to update menus now even if mode not changed */
  629.             force_menu_update = TRUE;
  630.         }
  631.         else
  632.         {
  633.             /* Sub-menu (not at end of path yet) */
  634.             gui_mch_add_menu(menu, new_idx);
  635.         }
  636.         }
  637.  
  638. # if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  639.         /* When adding a new submenu, may add a tearoff item */
  640.         if (    addtearoff
  641.             && *next_name
  642.             && vim_strchr(p_go, GO_TEAROFF) != NULL
  643.             && menu_is_menubar(name))
  644.         {
  645.         char_u        *tearpath;
  646.  
  647.         /*
  648.          * The pointers next_name & path_name refer to a string with
  649.          * \'s and ^V's stripped out. But menu_path is a "raw"
  650.          * string, so we must correct for special characters.
  651.          */
  652.         tearpath = alloc((unsigned int)STRLEN(menu_path) + TEAR_LEN + 2);
  653.         if (tearpath != NULL)
  654.         {
  655.             char_u  *s;
  656.             int        idx;
  657.  
  658.             STRCPY(tearpath, menu_path);
  659.             idx = (int)(next_name - path_name - 1);
  660.             for (s = tearpath; *s && s < tearpath + idx; ++s)
  661.             {
  662.             if ((*s == '\\' || *s == Ctrl_V) && s[1])
  663.             {
  664.                 ++idx;
  665.                 ++s;
  666.             }
  667. #  ifdef FEAT_MBYTE
  668.             if (has_mbyte)
  669.                 s += (*mb_ptr2len_check)(s) - 1;
  670. #  endif
  671.             }
  672.             tearpath[idx] = NUL;
  673.             gui_add_tearoff(tearpath, pri_tab, pri_idx);
  674.             vim_free(tearpath);
  675.         }
  676.         }
  677. # endif
  678.     }
  679. #endif /* FEAT_GUI */
  680.  
  681.     menup = &menu->children;
  682.     parent = menu;
  683.     name = next_name;
  684.     vim_free(dname);
  685.     if (pri_tab[pri_idx + 1] != -1)
  686.         ++pri_idx;
  687.     }
  688.     vim_free(path_name);
  689.  
  690.     /*
  691.      * Only add system menu items which have not been defined yet.
  692.      * First check if this was an ":amenu".
  693.      */
  694.     amenu = ((modes & (MENU_NORMAL_MODE | MENU_INSERT_MODE)) ==
  695.                        (MENU_NORMAL_MODE | MENU_INSERT_MODE));
  696.     if (sys_menu)
  697.     modes &= ~old_modes;
  698.  
  699.     if (menu != NULL && modes)
  700.     {
  701. #ifdef FEAT_GUI
  702.     menu->cb = gui_menu_cb;
  703. #endif
  704.     p = (call_data == NULL) ? NULL : vim_strsave(call_data);
  705.  
  706.     /* loop over all modes, may add more than one */
  707.     for (i = 0; i < MENU_MODES; ++i)
  708.     {
  709.         if (modes & (1 << i))
  710.         {
  711.         /* free any old menu */
  712.         free_menu_string(menu, i);
  713.  
  714.         /* For "amenu", may insert an extra character */
  715.         /* Don't do this if adding a tearbar (addtearoff == FALSE) */
  716.         c = 0;
  717.         if (amenu
  718. #ifdef FEAT_GUI_W32
  719.                && addtearoff
  720. #endif
  721.                        )
  722.         {
  723.             switch (1 << i)
  724.             {
  725.             case MENU_VISUAL_MODE:
  726.             case MENU_OP_PENDING_MODE:
  727.             case MENU_CMDLINE_MODE:
  728.                 c = Ctrl_C;
  729.                 break;
  730.             case MENU_INSERT_MODE:
  731.                 c = Ctrl_O;
  732.                 break;
  733.             }
  734.         }
  735.  
  736.         if (c)
  737.         {
  738.             menu->strings[i] = alloc((unsigned)(STRLEN(call_data) + 2));
  739.             if (menu->strings[i] != NULL)
  740.             {
  741.             menu->strings[i][0] = c;
  742.             STRCPY(menu->strings[i] + 1, call_data);
  743.             }
  744.         }
  745.         else
  746.             menu->strings[i] = p;
  747.         menu->noremap[i] = menuarg->noremap[0];
  748.         menu->silent[i] = menuarg->silent[0];
  749.         }
  750.     }
  751. #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
  752.     /* Need to update the menu tip. */
  753.     if (modes & MENU_TIP_MODE)
  754.         gui_mch_menu_set_tip(menu);
  755. #endif
  756.     }
  757.     return OK;
  758.  
  759. erret:
  760.     vim_free(path_name);
  761.     vim_free(dname);
  762.     return FAIL;
  763. }
  764.  
  765. /*
  766.  * Set the (sub)menu with the given name to enabled or disabled.
  767.  * Called recursively.
  768.  */
  769.     static int
  770. menu_nable_recurse(menu, name, modes, enable)
  771.     vimmenu_T    *menu;
  772.     char_u    *name;
  773.     int        modes;
  774.     int        enable;
  775. {
  776.     char_u    *p;
  777.  
  778.     if (menu == NULL)
  779.     return OK;        /* Got to bottom of hierarchy */
  780.  
  781.     /* Get name of this element in the menu hierarchy */
  782.     p = menu_name_skip(name);
  783.  
  784.     /* Find the menu */
  785.     while (menu != NULL)
  786.     {
  787.     if (*name == NUL || *name == '*' || menu_name_equal(name, menu))
  788.     {
  789.         if (*p != NUL)
  790.         {
  791.         if (menu->children == NULL)
  792.         {
  793.             EMSG(_(e_notsubmenu));
  794.             return FAIL;
  795.         }
  796.         if (menu_nable_recurse(menu->children, p, modes, enable)
  797.                                       == FAIL)
  798.             return FAIL;
  799.         }
  800.         else
  801.         if (enable)
  802.             menu->enabled |= modes;
  803.         else
  804.             menu->enabled &= ~modes;
  805.  
  806.         /*
  807.          * When name is empty, we are doing all menu items for the given
  808.          * modes, so keep looping, otherwise we are just doing the named
  809.          * menu item (which has been found) so break here.
  810.          */
  811.         if (*name != NUL && *name != '*')
  812.         break;
  813.     }
  814.     menu = menu->next;
  815.     }
  816.     if (*name != NUL && *name != '*' && menu == NULL)
  817.     {
  818.     EMSG(_(e_nomenu));
  819.     return FAIL;
  820.     }
  821.  
  822. #ifdef FEAT_GUI
  823.     /* Want to update menus now even if mode not changed */
  824.     force_menu_update = TRUE;
  825. #endif
  826.  
  827.     return OK;
  828. }
  829.  
  830. /*
  831.  * Remove the (sub)menu with the given name from the menu hierarchy
  832.  * Called recursively.
  833.  */
  834.     static int
  835. remove_menu(menup, name, modes, silent)
  836.     vimmenu_T    **menup;
  837.     char_u    *name;
  838.     int        modes;
  839.     int        silent;        /* don't give error messages */
  840. {
  841.     vimmenu_T    *menu;
  842.     vimmenu_T    *child;
  843.     char_u    *p;
  844.  
  845.     if (*menup == NULL)
  846.     return OK;        /* Got to bottom of hierarchy */
  847.  
  848.     /* Get name of this element in the menu hierarchy */
  849.     p = menu_name_skip(name);
  850.  
  851.     /* Find the menu */
  852.     while ((menu = *menup) != NULL)
  853.     {
  854.     if (*name == NUL || menu_name_equal(name, menu))
  855.     {
  856.         if (*p != NUL && menu->children == NULL)
  857.         {
  858.         if (!silent)
  859.             EMSG(_(e_notsubmenu));
  860.         return FAIL;
  861.         }
  862.         if ((menu->modes & modes) != 0x0)
  863.         {
  864. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  865.         /*
  866.          * If we are removing all entries for this menu,MENU_ALL_MODES,
  867.          * Then kill any tearoff before we start
  868.          */
  869.         if (*p == NUL && modes == MENU_ALL_MODES)
  870.         {
  871.             if (IsWindow(menu->tearoff_handle))
  872.             DestroyWindow(menu->tearoff_handle);
  873.         }
  874. #endif
  875.         if (remove_menu(&menu->children, p, modes, silent) == FAIL)
  876.             return FAIL;
  877.         }
  878.         else if (*name != NUL)
  879.         {
  880.         if (!silent)
  881.             EMSG(_(e_othermode));
  882.         return FAIL;
  883.         }
  884.  
  885.         /*
  886.          * When name is empty, we are removing all menu items for the given
  887.          * modes, so keep looping, otherwise we are just removing the named
  888.          * menu item (which has been found) so break here.
  889.          */
  890.         if (*name != NUL)
  891.         break;
  892.  
  893.         /* Remove the menu item for the given mode[s].  If the menu item
  894.          * is no longer valid in ANY mode, delete it */
  895.         menu->modes &= ~modes;
  896.         if (modes & MENU_TIP_MODE)
  897.         free_menu_string(menu, MENU_INDEX_TIP);
  898.         if ((menu->modes & MENU_ALL_MODES) == 0)
  899.         free_menu(menup);
  900.         else
  901.         menup = &menu->next;
  902.     }
  903.     else
  904.         menup = &menu->next;
  905.     }
  906.     if (*name != NUL)
  907.     {
  908.     if (menu == NULL)
  909.     {
  910.         if (!silent)
  911.         EMSG(_(e_nomenu));
  912.         return FAIL;
  913.     }
  914.  
  915.  
  916.     /* Recalculate modes for menu based on the new updated children */
  917.     menu->modes &= ~modes;
  918. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  919.     if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */
  920.         child = menu->children->next; /* don't count tearoff bar */
  921.     else
  922. #endif
  923.         child = menu->children;
  924.     for ( ; child != NULL; child = child->next)
  925.         menu->modes |= child->modes;
  926.     if (modes & MENU_TIP_MODE)
  927.     {
  928.         free_menu_string(menu, MENU_INDEX_TIP);
  929. #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
  930.         /* Need to update the menu tip. */
  931.         if (gui.in_use)
  932.         gui_mch_menu_set_tip(menu);
  933. #endif
  934.     }
  935.     if ((menu->modes & MENU_ALL_MODES) == 0)
  936.     {
  937.         /* The menu item is no longer valid in ANY mode, so delete it */
  938. #if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF)
  939.         if (s_tearoffs && menu->children != NULL) /* there's a tear bar.. */
  940.         free_menu(&menu->children);
  941. #endif
  942.         *menup = menu;
  943.         free_menu(menup);
  944.     }
  945.     }
  946.  
  947.     return OK;
  948. }
  949.  
  950. /*
  951.  * Free the given menu structure and remove it from the linked list.
  952.  */
  953.     static void
  954. free_menu(menup)
  955.     vimmenu_T    **menup;
  956. {
  957.     int        i;
  958.     vimmenu_T    *menu;
  959.  
  960.     menu = *menup;
  961.  
  962. #ifdef FEAT_GUI
  963.     /* Free machine specific menu structures (only when already created) */
  964.     /* Also may rebuild a tearoff'ed menu */
  965.     if (gui.in_use)
  966.     gui_mch_destroy_menu(menu);
  967. #endif
  968.  
  969.     /* Don't change *menup until after calling gui_mch_destroy_menu(). The
  970.      * MacOS code needs the original structure to properly delete the menu. */
  971.     *menup = menu->next;
  972.     vim_free(menu->name);
  973.     vim_free(menu->dname);
  974.     vim_free(menu->actext);
  975. #ifdef FEAT_TOOLBAR
  976.     vim_free(menu->iconfile);
  977. #endif
  978.     for (i = 0; i < MENU_MODES; i++)
  979.     free_menu_string(menu, i);
  980.     vim_free(menu);
  981.  
  982. #ifdef FEAT_GUI
  983.     /* Want to update menus now even if mode not changed */
  984.     force_menu_update = TRUE;
  985. #endif
  986. }
  987.  
  988. /*
  989.  * Free the menu->string with the given index.
  990.  */
  991.     static void
  992. free_menu_string(menu, idx)
  993.     vimmenu_T    *menu;
  994.     int        idx;
  995. {
  996.     int        count = 0;
  997.     int        i;
  998.  
  999.     for (i = 0; i < MENU_MODES; i++)
  1000.     if (menu->strings[i] == menu->strings[idx])
  1001.         count++;
  1002.     if (count == 1)
  1003.     vim_free(menu->strings[idx]);
  1004.     menu->strings[idx] = NULL;
  1005. }
  1006.  
  1007. /*
  1008.  * Show the mapping associated with a menu item or hierarchy in a sub-menu.
  1009.  */
  1010.     static int
  1011. show_menus(path_name, modes)
  1012.     char_u  *path_name;
  1013.     int        modes;
  1014. {
  1015.     char_u    *p;
  1016.     char_u    *name;
  1017.     vimmenu_T    *menu;
  1018.     vimmenu_T    *parent = NULL;
  1019.  
  1020.     menu = root_menu;
  1021.     name = path_name = vim_strsave(path_name);
  1022.     if (path_name == NULL)
  1023.     return FAIL;
  1024.  
  1025.     /* First, find the (sub)menu with the given name */
  1026.     while (*name)
  1027.     {
  1028.     p = menu_name_skip(name);
  1029.     while (menu != NULL)
  1030.     {
  1031.         if (menu_name_equal(name, menu))
  1032.         {
  1033.         /* Found menu */
  1034.         if (*p != NUL && menu->children == NULL)
  1035.         {
  1036.             EMSG(_(e_notsubmenu));
  1037.             vim_free(path_name);
  1038.             return FAIL;
  1039.         }
  1040.         else if ((menu->modes & modes) == 0x0)
  1041.         {
  1042.             EMSG(_(e_othermode));
  1043.             vim_free(path_name);
  1044.             return FAIL;
  1045.         }
  1046.         break;
  1047.         }
  1048.         menu = menu->next;
  1049.     }
  1050.     if (menu == NULL)
  1051.     {
  1052.         EMSG(_(e_nomenu));
  1053.         vim_free(path_name);
  1054.         return FAIL;
  1055.     }
  1056.     name = p;
  1057.     parent = menu;
  1058.     menu = menu->children;
  1059.     }
  1060.  
  1061.     /* Now we have found the matching menu, and we list the mappings */
  1062.                             /* Highlight title */
  1063.     MSG_PUTS_TITLE(_("\n--- Menus ---"));
  1064.  
  1065.     show_menus_recursive(parent, modes, 0);
  1066.     return OK;
  1067. }
  1068.  
  1069. /*
  1070.  * Recursively show the mappings associated with the menus under the given one
  1071.  */
  1072.     static void
  1073. show_menus_recursive(menu, modes, depth)
  1074.     vimmenu_T    *menu;
  1075.     int        modes;
  1076.     int        depth;
  1077. {
  1078.     int        i;
  1079.     int        bit;
  1080.  
  1081.     if (menu != NULL && (menu->modes & modes) == 0x0)
  1082.     return;
  1083.  
  1084.     if (menu != NULL)
  1085.     {
  1086.     msg_putchar('\n');
  1087.     if (got_int)        /* "q" hit for "--more--" */
  1088.         return;
  1089.     for (i = 0; i < depth; i++)
  1090.         MSG_PUTS("  ");
  1091.     if (menu->priority)
  1092.     {
  1093.         msg_outnum((long)menu->priority);
  1094.         MSG_PUTS(" ");
  1095.     }
  1096.                 /* Same highlighting as for directories!? */
  1097.     msg_outtrans_attr(menu->name, hl_attr(HLF_D));
  1098.     }
  1099.  
  1100.     if (menu != NULL && menu->children == NULL)
  1101.     {
  1102.     for (bit = 0; bit < MENU_MODES; bit++)
  1103.         if ((menu->modes & modes & (1 << bit)) != 0)
  1104.         {
  1105.         msg_putchar('\n');
  1106.         if (got_int)        /* "q" hit for "--more--" */
  1107.             return;
  1108.         for (i = 0; i < depth + 2; i++)
  1109.             MSG_PUTS("  ");
  1110.         msg_putchar(menu_mode_chars[bit]);
  1111.         if (menu->noremap[bit] == REMAP_NONE)
  1112.             msg_putchar('*');
  1113.         else if (menu->noremap[bit] == REMAP_SCRIPT)
  1114.             msg_putchar('&');
  1115.         else
  1116.             msg_putchar(' ');
  1117.         if (menu->silent[bit])
  1118.             msg_putchar('s');
  1119.         else
  1120.             msg_putchar(' ');
  1121.         if ((menu->modes & menu->enabled & (1 << bit)) == 0)
  1122.             msg_putchar('-');
  1123.         else
  1124.             msg_putchar(' ');
  1125.         MSG_PUTS(" ");
  1126.         msg_outtrans_special(menu->strings[bit], FALSE);
  1127.         }
  1128.     }
  1129.     else
  1130.     {
  1131.     if (menu == NULL)
  1132.     {
  1133.         menu = root_menu;
  1134.         depth--;
  1135.     }
  1136.     else
  1137.         menu = menu->children;
  1138.  
  1139.     /* recursively show all children.  Skip PopUp[nvoci]. */
  1140.     for (; menu != NULL && !got_int; menu = menu->next)
  1141.         if (!menu_is_hidden(menu->dname))
  1142.         show_menus_recursive(menu, modes, depth + 1);
  1143.     }
  1144. }
  1145.  
  1146. #ifdef FEAT_CMDL_COMPL
  1147.  
  1148. /*
  1149.  * Used when expanding menu names.
  1150.  */
  1151. static vimmenu_T    *expand_menu = NULL;
  1152. static int        expand_modes = 0x0;
  1153.  
  1154. /*
  1155.  * Work out what to complete when doing command line completion of menu names.
  1156.  */
  1157.     char_u *
  1158. set_context_in_menu_cmd(xp, cmd, arg, forceit)
  1159.     expand_T    *xp;
  1160.     char_u    *cmd;
  1161.     char_u    *arg;
  1162.     int        forceit;
  1163. {
  1164.     char_u    *after_dot;
  1165.     char_u    *p;
  1166.     char_u    *path_name = NULL;
  1167.     char_u    *name;
  1168.     int        unmenu;
  1169.     vimmenu_T    *menu;
  1170.     int        expand_menus;
  1171.  
  1172.     xp->xp_context = EXPAND_UNSUCCESSFUL;
  1173.  
  1174.  
  1175.     /* Check for priority numbers, enable and disable */
  1176.     for (p = arg; *p; ++p)
  1177.     if (!isdigit(*p) && *p != '.')
  1178.         break;
  1179.  
  1180.     if (!vim_iswhite(*p))
  1181.     {
  1182.     if (STRNCMP(arg, "enable", 6) == 0
  1183.         && (arg[6] == NUL ||  vim_iswhite(arg[6])))
  1184.         p = arg + 6;
  1185.     else if (STRNCMP(arg, "disable", 7) == 0
  1186.         && (arg[7] == NUL || vim_iswhite(arg[7])))
  1187.         p = arg + 7;
  1188.     else
  1189.         p = arg;
  1190.     }
  1191.  
  1192.     while (*p != NUL && vim_iswhite(*p))
  1193.     ++p;
  1194.  
  1195.     arg = after_dot = p;
  1196.  
  1197.     for (; *p && !vim_iswhite(*p); ++p)
  1198.     {
  1199.     if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
  1200.         p++;
  1201.     else if (*p == '.')
  1202.         after_dot = p + 1;
  1203.     }
  1204.     expand_menus = !(*cmd == 't' || *cmd == 'p');
  1205.     if (expand_menus  && vim_iswhite(*p))
  1206.     return NULL;    /* TODO: check for next command? */
  1207.     if (*p == NUL)        /* Complete the menu name */
  1208.     {
  1209.     /*
  1210.      * With :unmenu, you only want to match menus for the appropriate mode.
  1211.      * With :menu though you might want to add a menu with the same name as
  1212.      * one in another mode, so match menus from other modes too.
  1213.      */
  1214.     expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
  1215.     if (!unmenu)
  1216.         expand_modes = MENU_ALL_MODES;
  1217.  
  1218.     menu = root_menu;
  1219.     if (after_dot != arg)
  1220.     {
  1221.         path_name = alloc((unsigned)(after_dot - arg));
  1222.         if (path_name == NULL)
  1223.         return NULL;
  1224.         STRNCPY(path_name, arg, after_dot - arg - 1);
  1225.         path_name[after_dot - arg - 1] = NUL;
  1226.     }
  1227.     name = path_name;
  1228.     while (name != NULL && *name)
  1229.     {
  1230.         p = menu_name_skip(name);
  1231.         while (menu != NULL)
  1232.         {
  1233.         if (menu_name_equal(name, menu))
  1234.         {
  1235.             /* Found menu */
  1236.             if ((*p != NUL && menu->children == NULL)
  1237.             || ((menu->modes & expand_modes) == 0x0))
  1238.             {
  1239.             /*
  1240.              * Menu path continues, but we have reached a leaf.
  1241.              * Or menu exists only in another mode.
  1242.              */
  1243.             vim_free(path_name);
  1244.             return NULL;
  1245.             }
  1246.             break;
  1247.         }
  1248.         menu = menu->next;
  1249.         }
  1250.         if (menu == NULL)
  1251.         {
  1252.         /* No menu found with the name we were looking for */
  1253.         vim_free(path_name);
  1254.         return NULL;
  1255.         }
  1256.         name = p;
  1257.         menu = menu->children;
  1258.     }
  1259.  
  1260.     xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS;
  1261.     xp->xp_pattern = after_dot;
  1262.     expand_menu = menu;
  1263.     }
  1264.     else            /* We're in the mapping part */
  1265.     xp->xp_context = EXPAND_NOTHING;
  1266.     return NULL;
  1267. }
  1268.  
  1269. /*
  1270.  * Function given to ExpandGeneric() to obtain the list of group names.
  1271.  */
  1272.     char_u *
  1273. get_menu_name(xp, idx)
  1274.     expand_T    *xp;
  1275.     int        idx;
  1276. {
  1277.     static vimmenu_T    *menu = NULL;
  1278.     static int        get_dname = FALSE; /* return menu->dname next time */
  1279.     char_u        *str;
  1280.  
  1281.     if (idx == 0)        /* first call: start at first item */
  1282.     {
  1283.     menu = expand_menu;
  1284.     get_dname = FALSE;
  1285.     }
  1286.  
  1287.     /* Skip PopUp[nvoci]. */
  1288.     while (menu != NULL && (menu_is_hidden(menu->dname)
  1289. /*        || menu_is_separator(menu->dname) */
  1290.         || menu_is_tearoff(menu->dname)
  1291.         || (xp->xp_context == EXPAND_MENUS && menu->children == NULL)))
  1292.     menu = menu->next;
  1293.  
  1294.     if (menu == NULL)        /* at end of linked list */
  1295.     return NULL;
  1296.  
  1297.     if (menu->modes & expand_modes)
  1298.     {
  1299.     if (get_dname)
  1300.     {
  1301.         str = menu->dname;
  1302.         get_dname = FALSE;
  1303.     }
  1304.     else
  1305.     {
  1306.         str = menu->name;
  1307.         if (STRCMP(menu->name, menu->dname))
  1308.         get_dname = TRUE;
  1309.     }
  1310.     }
  1311.     else
  1312.     {
  1313.     str = (char_u *)"";
  1314.     get_dname = FALSE;
  1315.     }
  1316.  
  1317.     /* Advance to next menu entry. */
  1318.     if (!get_dname)
  1319.     menu = menu->next;
  1320.  
  1321.     return str;
  1322. }
  1323.  
  1324. /*
  1325.  * Function given to ExpandGeneric() to obtain the list of group names.
  1326.  */
  1327.     char_u *
  1328. get_menu_names(xp, idx)
  1329.     expand_T    *xp;
  1330.     int        idx;
  1331. {
  1332.     static vimmenu_T    *menu = NULL;
  1333.     static char_u    tbuffer[256]; /*hack*/
  1334.     char_u        *str;
  1335.  
  1336.     if (idx == 0)        /* first call: start at first item */
  1337.     menu = expand_menu;
  1338.  
  1339.     /* Skip Browse-style entries, popup menus and separators. */
  1340.     while (menu != NULL
  1341.         && (  menu_is_hidden(menu->dname)
  1342. /*        || menu_is_separator(menu->dname) */
  1343.         || menu_is_tearoff(menu->dname)
  1344.         || (xp->xp_context == EXPAND_MENUS && menu->children == NULL)
  1345. #ifndef FEAT_BROWSE
  1346.         || menu->dname[STRLEN(menu->dname) - 1] == '.'
  1347. #endif
  1348.            ))
  1349.     menu = menu->next;
  1350.  
  1351.     if (menu == NULL)        /* at end of linked list */
  1352.     return NULL;
  1353.  
  1354.     if (menu->modes & expand_modes)
  1355.     {
  1356.     if (menu->children != NULL)
  1357.     {
  1358.         STRCPY(tbuffer, menu->dname);
  1359.         /* hack on menu separators:  use a 'magic' char for the separator
  1360.          * so that '.' in names gets escaped properly */
  1361.         STRCAT(tbuffer, "\001");
  1362.         str = tbuffer;
  1363.     }
  1364.     else
  1365.         str = menu->dname;
  1366.     }
  1367.     else
  1368.     str = (char_u *)"";
  1369.  
  1370.     /* Advance to next menu entry. */
  1371.     menu = menu->next;
  1372.  
  1373.     return str;
  1374. }
  1375. #endif /* FEAT_CMDL_COMPL */
  1376.  
  1377. /*
  1378.  * Skip over this element of the menu path and return the start of the next
  1379.  * element.  Any \ and ^Vs are removed from the current element.
  1380.  */
  1381.     char_u *
  1382. menu_name_skip(name)
  1383.     char_u  *name;
  1384. {
  1385.     char_u  *p;
  1386.  
  1387.     for (p = name; *p && *p != '.'; p++)
  1388.     {
  1389.     if (*p == '\\' || *p == Ctrl_V)
  1390.     {
  1391.         mch_memmove(p, p + 1, STRLEN(p));
  1392.         if (*p == NUL)
  1393.         break;
  1394.     }
  1395. #ifdef FEAT_MBYTE
  1396.     if (has_mbyte)
  1397.         p += (*mb_ptr2len_check)(p) - 1;    /* skip multibyte char */
  1398. #endif
  1399.     }
  1400.     if (*p)
  1401.     *p++ = NUL;
  1402.     return p;
  1403. }
  1404.  
  1405. /*
  1406.  * Return TRUE when "name" matches with menu "menu".  The name is compared in
  1407.  * two ways: raw menu name and menu name without '&'.  ignore part after a TAB.
  1408.  */
  1409.     static int
  1410. menu_name_equal(name, menu)
  1411.     char_u    *name;
  1412.     vimmenu_T    *menu;
  1413. {
  1414.     return (menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname));
  1415. }
  1416.  
  1417.     static int
  1418. menu_namecmp(name, mname)
  1419.     char_u    *name;
  1420.     char_u    *mname;
  1421. {
  1422.     int        i;
  1423.  
  1424.     for (i = 0; name[i] != NUL && name[i] != TAB; ++i)
  1425.     if (name[i] != mname[i])
  1426.         break;
  1427.     return ((name[i] == NUL || name[i] == TAB)
  1428.         && (mname[i] == NUL || mname[i] == TAB));
  1429. }
  1430.  
  1431. /*
  1432.  * Return the modes specified by the given menu command (eg :menu! returns
  1433.  * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
  1434.  * If "noremap" is not NULL, then the flag it points to is set according to
  1435.  * whether the command is a "nore" command.
  1436.  * If "unmenu" is not NULL, then the flag it points to is set according to
  1437.  * whether the command is an "unmenu" command.
  1438.  */
  1439.     static int
  1440. get_menu_cmd_modes(cmd, forceit, noremap, unmenu)
  1441.     char_u  *cmd;
  1442.     int        forceit;        /* Was there a "!" after the command? */
  1443.     int        *noremap;
  1444.     int        *unmenu;
  1445. {
  1446.     int        modes;
  1447.  
  1448.     switch (*cmd++)
  1449.     {
  1450.     case 'v':            /* vmenu, vunmenu, vnoremenu */
  1451.         modes = MENU_VISUAL_MODE;
  1452.         break;
  1453.     case 'o':            /* omenu */
  1454.         modes = MENU_OP_PENDING_MODE;
  1455.         break;
  1456.     case 'i':            /* imenu */
  1457.         modes = MENU_INSERT_MODE;
  1458.         break;
  1459.     case 't':
  1460.         modes = MENU_TIP_MODE;    /* tmenu */
  1461.         break;
  1462.     case 'c':            /* cmenu */
  1463.         modes = MENU_CMDLINE_MODE;
  1464.         break;
  1465.     case 'a':            /* amenu */
  1466.         modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE | MENU_NORMAL_MODE
  1467.                     | MENU_VISUAL_MODE | MENU_OP_PENDING_MODE;
  1468.         break;
  1469.     case 'n':
  1470.         if (cmd[1] != 'o')        /* nmenu */
  1471.         {
  1472.         modes = MENU_NORMAL_MODE;
  1473.         break;
  1474.         }
  1475.         /* FALLTHROUGH */
  1476.     default:
  1477.         --cmd;
  1478.         if (forceit)        /* menu!! */
  1479.         modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
  1480.         else            /* menu */
  1481.         modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE
  1482.                                | MENU_OP_PENDING_MODE;
  1483.     }
  1484.  
  1485.     if (noremap != NULL)
  1486.     *noremap = (*cmd == 'n' ? REMAP_NONE : REMAP_YES);
  1487.     if (unmenu != NULL)
  1488.     *unmenu = (*cmd == 'u');
  1489.     return modes;
  1490. }
  1491.  
  1492. /*
  1493.  * Modify a menu name starting with "PopUp" to include the mode character.
  1494.  * Returns the name in allocated memory (NULL for failure).
  1495.  */
  1496.     static char_u *
  1497. popup_mode_name(name, idx)
  1498.     char_u    *name;
  1499.     int        idx;
  1500. {
  1501.     char_u    *p;
  1502.     int        len = (int)STRLEN(name);
  1503.  
  1504.     p = vim_strnsave(name, len + 1);
  1505.     if (p != NULL)
  1506.     {
  1507.     mch_memmove(p + 6, p + 5, (size_t)(len - 4));
  1508.     p[5] = menu_mode_chars[idx];
  1509.     }
  1510.     return p;
  1511. }
  1512.  
  1513. #if defined(FEAT_GUI) || defined(PROTO)
  1514. /*
  1515.  * Return the index into the menu->strings or menu->noremap arrays for the
  1516.  * current state.  Returns MENU_INDEX_INVALID if there is no mapping for the
  1517.  * given menu in the current mode.
  1518.  */
  1519.     int
  1520. get_menu_index(menu, state)
  1521.     vimmenu_T    *menu;
  1522.     int        state;
  1523. {
  1524.     int        idx;
  1525.  
  1526. #ifdef FEAT_VISUAL
  1527.     if (VIsual_active)
  1528.     idx = MENU_INDEX_VISUAL;
  1529.     else
  1530. #endif
  1531.     if ((state & INSERT))
  1532.     idx = MENU_INDEX_INSERT;
  1533.     else if ((state & CMDLINE) || state == HITRETURN || state == ASKMORE)
  1534.     idx = MENU_INDEX_CMDLINE;
  1535.     else if (finish_op)
  1536.     idx = MENU_INDEX_OP_PENDING;
  1537.     else if ((state & NORMAL))
  1538.     idx = MENU_INDEX_NORMAL;
  1539.     else
  1540.     idx = MENU_INDEX_INVALID;
  1541.  
  1542.     if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
  1543.     idx = MENU_INDEX_INVALID;
  1544.     return idx;
  1545. }
  1546. #endif
  1547.  
  1548. /*
  1549.  * Duplicate the menu item text and then process to see if a mnemonic key
  1550.  * and/or accelerator text has been identified.
  1551.  * Returns a pointer to allocated memory, or NULL for failure.
  1552.  * If mnemonic != NULL, *mnemonic is set to the character after the first '&'.
  1553.  * If actext != NULL, *actext is set to the text after the first TAB.
  1554.  */
  1555.     static char_u *
  1556. menu_text(str, mnemonic, actext)
  1557.     char_u    *str;
  1558.     int        *mnemonic;
  1559.     char_u    **actext;
  1560. {
  1561.     char_u    *p;
  1562.     char_u    *text;
  1563.  
  1564.     /* Locate accelerator text, after the first TAB */
  1565.     p = vim_strchr(str, TAB);
  1566.     if (p != NULL)
  1567.     {
  1568.     if (actext != NULL)
  1569.         *actext = vim_strsave(p + 1);
  1570.     text = vim_strnsave(str, (int)(p - str));
  1571.     }
  1572.     else
  1573.     text = vim_strsave(str);
  1574.     if (text != NULL)
  1575.     {
  1576.     p = vim_strchr(text, '&');
  1577.     if (p != NULL)
  1578.     {
  1579.         if (mnemonic != NULL)
  1580. #if !defined(__MVS__) || defined(MOTIF390_MNEMONIC_FIXED)
  1581.         *mnemonic = p[1];
  1582. #else
  1583.         {
  1584.         /*
  1585.          * Well there is a bug in the Motif libraries on OS390 Unix.
  1586.          * The mnemonic keys needs to be converted to ASCII values
  1587.          * first.
  1588.          * This behavior has been seen in 2.8 and 2.9.
  1589.          */
  1590.         char c = p[1];
  1591.         __etoa_l(&c, 1);
  1592.         *mnemonic = c;
  1593.         }
  1594. #endif
  1595.         mch_memmove(p, p + 1, STRLEN(p));
  1596.     }
  1597.     }
  1598.     return text;
  1599. }
  1600.  
  1601. /*
  1602.  * Return TRUE if "name" can be a menu in the MenuBar.
  1603.  */
  1604.     int
  1605. menu_is_menubar(name)
  1606.     char_u    *name;
  1607. {
  1608.     return (!menu_is_popup(name)
  1609.         && !menu_is_toolbar(name)
  1610.         && *name != MNU_HIDDEN_CHAR);
  1611. }
  1612.  
  1613. /*
  1614.  * Return TRUE if "name" is a popup menu name.
  1615.  */
  1616.     int
  1617. menu_is_popup(name)
  1618.     char_u    *name;
  1619. {
  1620.     return (STRNCMP(name, "PopUp", 5) == 0);
  1621. }
  1622.  
  1623. #if defined(FEAT_GUI_MOTIF) || defined(PROTO)
  1624. /*
  1625.  * Return TRUE if "name" is part of a poup menu.
  1626.  */
  1627.     int
  1628. menu_is_child_of_popup(menu)
  1629.     vimmenu_T *menu;
  1630. {
  1631.     while (menu->parent != NULL)
  1632.     menu = menu->parent;
  1633.     return menu_is_popup(menu->name);
  1634. }
  1635. #endif
  1636.  
  1637. /*
  1638.  * Return TRUE if "name" is a toolbar menu name.
  1639.  */
  1640.     int
  1641. menu_is_toolbar(name)
  1642.     char_u    *name;
  1643. {
  1644.     return (STRNCMP(name, "ToolBar", 7) == 0);
  1645. }
  1646.  
  1647. /*
  1648.  * Return TRUE if the name is a menu separator identifier: Starts and ends
  1649.  * with '-'
  1650.  */
  1651.     int
  1652. menu_is_separator(name)
  1653.     char_u *name;
  1654. {
  1655.     return (name[0] == '-' && name[STRLEN(name) - 1] == '-');
  1656. }
  1657.  
  1658. /*
  1659.  * Return TRUE if the menu is hidden:  Starts with ']'
  1660.  */
  1661.     static int
  1662. menu_is_hidden(name)
  1663.     char_u *name;
  1664. {
  1665.     return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
  1666. }
  1667.  
  1668. #if defined(FEAT_CMDL_COMPL) \
  1669.     || (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF))
  1670. /*
  1671.  * Return TRUE if the menu is the tearoff menu.
  1672.  */
  1673. /*ARGSUSED*/
  1674.     static int
  1675. menu_is_tearoff(name)
  1676.     char_u *name;
  1677. {
  1678. #ifdef FEAT_GUI
  1679.     return (STRCMP(name, TEAR_STRING) == 0);
  1680. #else
  1681.     return FALSE;
  1682. #endif
  1683. }
  1684. #endif
  1685.  
  1686. #ifdef FEAT_GUI
  1687.  
  1688.     static int
  1689. get_menu_mode()
  1690. {
  1691. #ifdef FEAT_VISUAL
  1692.     if (VIsual_active)
  1693.     return MENU_INDEX_VISUAL;
  1694. #endif
  1695.     if (State & INSERT)
  1696.     return MENU_INDEX_INSERT;
  1697.     if ((State & CMDLINE) || State == ASKMORE || State == HITRETURN)
  1698.     return MENU_INDEX_CMDLINE;
  1699.     if (finish_op)
  1700.     return MENU_INDEX_OP_PENDING;
  1701.     if (State & NORMAL)
  1702.     return MENU_INDEX_NORMAL;
  1703.     if (State & LANGMAP)    /* must be a "r" command, like Insert mode */
  1704.     return MENU_INDEX_INSERT;
  1705.     return MENU_INDEX_INVALID;
  1706. }
  1707.  
  1708. /*
  1709.  * After we have started the GUI, then we can create any menus that have been
  1710.  * defined.  This is done once here.  add_menu_path() may have already been
  1711.  * called to define these menus, and may be called again.  This function calls
  1712.  * itself recursively.    Should be called at the top level with:
  1713.  * gui_create_initial_menus(root_menu, NULL);
  1714.  */
  1715.     void
  1716. gui_create_initial_menus(menu)
  1717.     vimmenu_T    *menu;
  1718. {
  1719.     int        idx = 0;
  1720.  
  1721.     while (menu != NULL)
  1722.     {
  1723.     /* Don't add a menu when only a tip was defined. */
  1724.     if (menu->modes & MENU_ALL_MODES)
  1725.     {
  1726.         if (menu->children != NULL)
  1727.         {
  1728.         gui_mch_add_menu(menu, idx);
  1729.         gui_create_initial_menus(menu->children);
  1730.         }
  1731.         else
  1732.         gui_mch_add_menu_item(menu, idx);
  1733.     }
  1734.     menu = menu->next;
  1735.     ++idx;
  1736.     }
  1737. }
  1738.  
  1739. /*
  1740.  * Used recursively by gui_update_menus (see below)
  1741.  */
  1742.     static void
  1743. gui_update_menus_recurse(menu, mode)
  1744.     vimmenu_T    *menu;
  1745.     int        mode;
  1746. {
  1747.     int        grey;
  1748.  
  1749.     while (menu)
  1750.     {
  1751.     if ((menu->modes & menu->enabled & mode)
  1752. #if defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)
  1753.         || menu_is_tearoff(menu->dname)
  1754. #endif
  1755.        )
  1756.         grey = FALSE;
  1757.     else
  1758.         grey = TRUE;
  1759. #ifdef FEAT_GUI_ATHENA
  1760.     /* Hiding menus doesn't work for Athena, it can cause a crash. */
  1761.     gui_mch_menu_grey(menu, grey);
  1762. #else
  1763.     /* Never hide a toplevel menu, it may make the menubar resize or
  1764.      * disappear. Same problem for ToolBar items. */
  1765.     if (vim_strchr(p_go, GO_GREY) != NULL || menu->parent == NULL
  1766. # ifdef FEAT_TOOLBAR
  1767.         || menu_is_toolbar(menu->parent->name)
  1768. # endif
  1769.            )
  1770.         gui_mch_menu_grey(menu, grey);
  1771.     else
  1772.         gui_mch_menu_hidden(menu, grey);
  1773. #endif
  1774.     gui_update_menus_recurse(menu->children, mode);
  1775.     menu = menu->next;
  1776.     }
  1777. }
  1778.  
  1779. /*
  1780.  * Make sure only the valid menu items appear for this mode.  If
  1781.  * force_menu_update is not TRUE, then we only do this if the mode has changed
  1782.  * since last time.  If "modes" is not 0, then we use these modes instead.
  1783.  */
  1784.     void
  1785. gui_update_menus(modes)
  1786.     int        modes;
  1787. {
  1788.     static int        prev_mode = -1;
  1789.     int            mode = 0;
  1790.  
  1791.     if (modes != 0x0)
  1792.     mode = modes;
  1793.     else
  1794.     {
  1795.     mode = get_menu_mode();
  1796.     if (mode == MENU_INDEX_INVALID)
  1797.         mode = 0;
  1798.     else
  1799.         mode = (1 << mode);
  1800.     }
  1801.  
  1802.     if (force_menu_update || mode != prev_mode)
  1803.     {
  1804.     gui_update_menus_recurse(root_menu, mode);
  1805.     gui_mch_draw_menubar();
  1806.     prev_mode = mode;
  1807.     force_menu_update = FALSE;
  1808. #ifdef FEAT_GUI_W32
  1809.     /* This can leave a tearoff as active window - make sure we
  1810.      * have the focus <negri>*/
  1811.     gui_mch_activate_window();
  1812. #endif
  1813.     }
  1814. }
  1815.  
  1816. #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \
  1817.     || defined(FEAT_GUI_PHOTON) || defined(PROTO)
  1818. /*
  1819.  * Check if a key is used as a mnemonic for a toplevel menu.
  1820.  * Case of the key is ignored.
  1821.  */
  1822.     int
  1823. gui_is_menu_shortcut(key)
  1824.     int        key;
  1825. {
  1826.     vimmenu_T    *menu;
  1827.  
  1828.     if (key < 256)
  1829.     key = TO_LOWER(key);
  1830.     for (menu = root_menu; menu != NULL; menu = menu->next)
  1831.     if (menu->mnemonic == key
  1832.         || (menu->mnemonic < 256 && TO_LOWER(menu->mnemonic) == key))
  1833.         return TRUE;
  1834.     return FALSE;
  1835. }
  1836. #endif
  1837.  
  1838. /*
  1839.  * Display the Special "PopUp" menu as a pop-up at the current mouse
  1840.  * position.  The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
  1841.  * etc.
  1842.  */
  1843.     void
  1844. gui_show_popupmenu()
  1845. {
  1846.     vimmenu_T    *menu;
  1847.     int        mode;
  1848.  
  1849.     mode = get_menu_mode();
  1850.     if (mode == MENU_INDEX_INVALID)
  1851.     return;
  1852.     mode = menu_mode_chars[mode];
  1853.  
  1854.     for (menu = root_menu; menu != NULL; menu = menu->next)
  1855.     if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode)
  1856.         break;
  1857.  
  1858.     /* Only show a popup when it is defined and has entries */
  1859.     if (menu != NULL && menu->children != NULL)
  1860.     gui_mch_show_popupmenu(menu);
  1861. }
  1862. #endif /* FEAT_GUI */
  1863.  
  1864. #if (defined(FEAT_GUI_W32) && defined(FEAT_TEAROFF)) || defined(PROTO)
  1865.  
  1866. /*
  1867.  * Deal with tearoff items that are added like a menu item.
  1868.  * Currently only for Win32 GUI.  Others may follow later.
  1869.  */
  1870.  
  1871.     void
  1872. gui_mch_toggle_tearoffs(int enable)
  1873. {
  1874.     int        pri_tab[MENUDEPTH + 1];
  1875.     int        i;
  1876.  
  1877.     if (enable)
  1878.     {
  1879.     for (i = 0; i < MENUDEPTH; ++i)
  1880.         pri_tab[i] = 500;
  1881.     pri_tab[MENUDEPTH] = -1;
  1882.     gui_create_tearoffs_recurse(root_menu, (char_u *)"", pri_tab, 0);
  1883.     }
  1884.     else
  1885.     gui_destroy_tearoffs_recurse(root_menu);
  1886.     s_tearoffs = enable;
  1887. }
  1888.  
  1889. /*
  1890.  * Recursively add tearoff items
  1891.  */
  1892.     static void
  1893. gui_create_tearoffs_recurse(menu, pname, pri_tab, pri_idx)
  1894.     vimmenu_T        *menu;
  1895.     const char_u    *pname;
  1896.     int            *pri_tab;
  1897.     int            pri_idx;
  1898. {
  1899.     char_u    *newpname = NULL;
  1900.     int        len;
  1901.     char_u    *s;
  1902.     char_u    *d;
  1903.  
  1904.     if (pri_tab[pri_idx + 1] != -1)
  1905.     ++pri_idx;
  1906.     while (menu != NULL)
  1907.     {
  1908.     if (menu->children != NULL && menu_is_menubar(menu->name))
  1909.     {
  1910.         /* Add the menu name to the menu path.  Insert a backslash before
  1911.          * dots (it's used to separate menu names). */
  1912.         len = (int)STRLEN(pname) + (int)STRLEN(menu->name);
  1913.         for (s = menu->name; *s; ++s)
  1914.         if (*s == '.' || *s == '\\')
  1915.             ++len;
  1916.         newpname = alloc(len + TEAR_LEN + 2);
  1917.         if (newpname != NULL)
  1918.         {
  1919.         STRCPY(newpname, pname);
  1920.         d = newpname + STRLEN(newpname);
  1921.         for (s = menu->name; *s; ++s)
  1922.         {
  1923.             if (*s == '.' || *s == '\\')
  1924.             *d++ = '\\';
  1925.             *d++ = *s;
  1926.         }
  1927.         *d = NUL;
  1928.  
  1929.         /* check if tearoff already exists */
  1930.         if (STRCMP(menu->children->name, TEAR_STRING) != 0)
  1931.         {
  1932.             gui_add_tearoff(newpname, pri_tab, pri_idx - 1);
  1933.             *d = NUL;            /* remove TEAR_STRING */
  1934.         }
  1935.  
  1936.         STRCAT(newpname, ".");
  1937.         gui_create_tearoffs_recurse(menu->children, newpname,
  1938.                                 pri_tab, pri_idx);
  1939.         vim_free(newpname);
  1940.         }
  1941.     }
  1942.     menu = menu->next;
  1943.     }
  1944. }
  1945.  
  1946. /*
  1947.  * Add tear-off menu item for a submenu.
  1948.  * "tearpath" is the menu path, and must have room to add TEAR_STRING.
  1949.  */
  1950.     static void
  1951. gui_add_tearoff(tearpath, pri_tab, pri_idx)
  1952.     char_u    *tearpath;
  1953.     int        *pri_tab;
  1954.     int        pri_idx;
  1955. {
  1956.     char_u    *tbuf;
  1957.     int        t;
  1958.     vimmenu_T    menuarg;
  1959.  
  1960.     tbuf = alloc(5 + (unsigned int)STRLEN(tearpath));
  1961.     if (tbuf != NULL)
  1962.     {
  1963.     tbuf[0] = K_SPECIAL;
  1964.     tbuf[1] = K_SECOND(K_TEAROFF);
  1965.     tbuf[2] = K_THIRD(K_TEAROFF);
  1966.     STRCPY(tbuf + 3, tearpath);
  1967.     STRCAT(tbuf + 3, "\r");
  1968.  
  1969.     STRCAT(tearpath, ".");
  1970.     STRCAT(tearpath, TEAR_STRING);
  1971.  
  1972.     /* Priority of tear-off is always 1 */
  1973.     t = pri_tab[pri_idx + 1];
  1974.     pri_tab[pri_idx + 1] = 1;
  1975.  
  1976. #ifdef FEAT_TOOLBAR
  1977.     menuarg.iconfile = NULL;
  1978.     menuarg.iconidx = -1;
  1979.     menuarg.icon_builtin = FALSE;
  1980. #endif
  1981.     menuarg.noremap[0] = REMAP_NONE;
  1982.     menuarg.silent[0] = TRUE;
  1983.  
  1984.     menuarg.modes = MENU_ALL_MODES;
  1985.     add_menu_path(tearpath, &menuarg, pri_tab, tbuf, FALSE);
  1986.  
  1987.     menuarg.modes = MENU_TIP_MODE;
  1988.     add_menu_path(tearpath, &menuarg, pri_tab,
  1989.         (char_u *)_("Tear off this menu"), FALSE);
  1990.  
  1991.     pri_tab[pri_idx + 1] = t;
  1992.     vim_free(tbuf);
  1993.     }
  1994. }
  1995.  
  1996. /*
  1997.  * Recursively destroy tearoff items
  1998.  */
  1999.     static void
  2000. gui_destroy_tearoffs_recurse(menu)
  2001.     vimmenu_T    *menu;
  2002. {
  2003.     while (menu)
  2004.     {
  2005.     if (menu->children)
  2006.     {
  2007.         /* check if tearoff exists */
  2008.         if (STRCMP(menu->children->name, TEAR_STRING) == 0)
  2009.         {
  2010.         /* Disconnect the item and free the memory */
  2011.         free_menu(&menu->children);
  2012.         }
  2013.         if (menu->children != NULL) /* if not the last one */
  2014.         gui_destroy_tearoffs_recurse(menu->children);
  2015.     }
  2016.     menu = menu->next;
  2017.     }
  2018. }
  2019.  
  2020. #endif /* FEAT_GUI_W32 && FEAT_TEAROFF */
  2021.  
  2022. /*
  2023.  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and
  2024.  * execute it.
  2025.  */
  2026.     void
  2027. ex_emenu(eap)
  2028.     exarg_T    *eap;
  2029. {
  2030.     vimmenu_T    *menu;
  2031.     char_u    *name;
  2032.     char_u    *saved_name;
  2033.     char_u    *p;
  2034.     int        idx;
  2035.     char_u    *mode;
  2036.  
  2037.     saved_name = vim_strsave(eap->arg);
  2038.     if (saved_name == NULL)
  2039.     return;
  2040.  
  2041.     menu = root_menu;
  2042.     name = saved_name;
  2043.     while (*name)
  2044.     {
  2045.     /* Find in the menu hierarchy */
  2046.     p = menu_name_skip(name);
  2047.  
  2048.     while (menu != NULL)
  2049.     {
  2050.         if (menu_name_equal(name, menu))
  2051.         {
  2052.         if (*p == NUL && menu->children != NULL)
  2053.         {
  2054.             EMSG(_("E333: Menu path must lead to a menu item"));
  2055.             menu = NULL;
  2056.         }
  2057.         else if (*p != NUL && menu->children == NULL)
  2058.         {
  2059.             EMSG(_(e_notsubmenu));
  2060.             menu = NULL;
  2061.         }
  2062.         break;
  2063.         }
  2064.         menu = menu->next;
  2065.     }
  2066.     if (menu == NULL || *p == NUL)
  2067.         break;
  2068.     menu = menu->children;
  2069.     name = p;
  2070.     }
  2071.     vim_free(saved_name);
  2072.     if (menu == NULL)
  2073.     {
  2074.     EMSG2(_("E334: Menu not found: %s"), eap->arg);
  2075.     return;
  2076.     }
  2077.  
  2078.     /* Found the menu, so execute. */
  2079.     if (restart_edit)
  2080.     {
  2081.     mode = (char_u *)"Insert";
  2082.     idx = MENU_INDEX_INSERT;
  2083.     }
  2084.     else if (eap->addr_count)
  2085.     {
  2086.     pos_T    tpos;
  2087.  
  2088.     mode = (char_u *)"Visual";
  2089.     idx = MENU_INDEX_VISUAL;
  2090.  
  2091.     /* GEDDES: This is not perfect - but it is a
  2092.      * quick way of detecting whether we are doing this from a
  2093.      * selection - see if the range matches up with the visual
  2094.      * select start and end.
  2095.      */
  2096.     if ((curbuf->b_visual_start.lnum == eap->line1)
  2097.         && (curbuf->b_visual_end.lnum) == eap->line2)
  2098.     {
  2099.         /* Set it up for visual mode - equivalent to gv.  */
  2100.         VIsual_mode = curbuf->b_visual_mode;
  2101.         tpos = curbuf->b_visual_end;
  2102.         curwin->w_cursor = curbuf->b_visual_start;
  2103.         curwin->w_curswant = curbuf->b_visual_curswant;
  2104.     }
  2105.     else
  2106.     {
  2107.         /* Set it up for line-wise visual mode */
  2108.         VIsual_mode = 'V';
  2109.         curwin->w_cursor.lnum = eap->line1;
  2110.         curwin->w_cursor.col = 1;
  2111.         tpos.lnum = eap->line2;
  2112.         tpos.col = MAXCOL;
  2113.     }
  2114.  
  2115.     /* Activate visual mode
  2116.      */
  2117.     VIsual_active = TRUE;
  2118.     VIsual_reselect = TRUE;
  2119.     check_cursor();
  2120.     VIsual = curwin->w_cursor;
  2121.     curwin->w_cursor = tpos;
  2122.  
  2123.     check_cursor();
  2124.  
  2125.     /* Adjust the cursor to make sure it is in the correct pos
  2126.      * for exclusive mode
  2127.      */
  2128.     if (*p_sel == 'e' && gchar_cursor() != NUL)
  2129.         ++curwin->w_cursor.col;
  2130.     }
  2131.     else
  2132.     {
  2133.     mode = (char_u *)"Normal";
  2134.     idx = MENU_INDEX_NORMAL;
  2135.     }
  2136.  
  2137.     if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL)
  2138.     {
  2139.     ins_typebuf(menu->strings[idx], menu->noremap[idx], 0,
  2140.                              TRUE, menu->silent[idx]);
  2141.     }
  2142.     else
  2143.     EMSG2(_("E335: Menu not defined for %s mode"), mode);
  2144. }
  2145.  
  2146. #if defined(FEAT_GUI_MSWIN) || (defined(FEAT_BEVAL) \
  2147.         && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))) \
  2148.     || defined(PROTO)
  2149. /*
  2150.  * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
  2151.  */
  2152.     vimmenu_T *
  2153. gui_find_menu(path_name)
  2154.     char_u *path_name;
  2155. {
  2156.     vimmenu_T    *menu = NULL;
  2157.     char_u    *name;
  2158.     char_u    *saved_name;
  2159.     char_u    *p;
  2160.  
  2161.     menu = root_menu;
  2162.  
  2163.     saved_name = vim_strsave(path_name);
  2164.     if (saved_name == NULL)
  2165.     return NULL;
  2166.  
  2167.     name = saved_name;
  2168.     while (*name)
  2169.     {
  2170.     /* find the end of one dot-separated name and put a NUL at the dot */
  2171.     p = menu_name_skip(name);
  2172.  
  2173.     while (menu != NULL)
  2174.     {
  2175.         if (STRCMP(name, menu->name) == 0 || STRCMP(name, menu->dname) == 0)
  2176.         {
  2177.         if (menu->children == NULL)
  2178.         {
  2179.             /* found a menu item instead of a sub-menu */
  2180.             if (*p == NUL)
  2181.             EMSG(_("E336: Menu path must lead to a sub-menu"));
  2182.             else
  2183.             EMSG(_(e_notsubmenu));
  2184.             menu = NULL;
  2185.             goto theend;
  2186.         }
  2187.         if (*p == NUL)        /* found a full match */
  2188.             goto theend;
  2189.         break;
  2190.         }
  2191.         menu = menu->next;
  2192.     }
  2193.     if (menu == NULL)    /* didn't find it */
  2194.         break;
  2195.  
  2196.     /* Found a match, search the sub-menu. */
  2197.     menu = menu->children;
  2198.     name = p;
  2199.     }
  2200.  
  2201.     if (menu == NULL)
  2202.     EMSG(_("E337: Menu not found - check menu names"));
  2203. theend:
  2204.     vim_free(saved_name);
  2205.     return menu;
  2206. }
  2207. #endif
  2208.  
  2209. #ifdef FEAT_MULTI_LANG
  2210. /*
  2211.  * Translation of menu names.  Just a simple lookup table.
  2212.  */
  2213.  
  2214. typedef struct
  2215. {
  2216.     char_u    *from;        /* English name */
  2217.     char_u    *to;        /* translated name */
  2218. } menutrans_T;
  2219.  
  2220. static garray_T menutrans_ga = {0, 0, 0, 0, NULL};
  2221. #endif
  2222.  
  2223. /*
  2224.  * ":menutrans".
  2225.  * This function is also defined without the +multi_lang feature, in which
  2226.  * case the commands are ignored.
  2227.  */
  2228.     void
  2229. ex_menutranslate(eap)
  2230.     exarg_T    *eap;
  2231. {
  2232. #ifdef FEAT_MULTI_LANG
  2233.     char_u        *arg = eap->arg;
  2234.     menutrans_T        *tp;
  2235.     int            i;
  2236.     char_u        *from, *to;
  2237.  
  2238.     if (menutrans_ga.ga_itemsize == 0)
  2239.     ga_init2(&menutrans_ga, (int)sizeof(menutrans_T), 5);
  2240.  
  2241.     /*
  2242.      * ":menutrans clear": clear all translations.
  2243.      */
  2244.     if (STRNCMP(arg, "clear", 5) == 0 && ends_excmd(*skipwhite(arg + 5)))
  2245.     {
  2246.     tp = (menutrans_T *)menutrans_ga.ga_data;
  2247.     for (i = 0; i < menutrans_ga.ga_len; ++i)
  2248.     {
  2249.         vim_free(tp[i].from);
  2250.         vim_free(tp[i].to);
  2251.     }
  2252.     ga_clear(&menutrans_ga);
  2253. # ifdef FEAT_EVAL
  2254.     /* Delete all "menutrans_" global variables. */
  2255.     del_menutrans_vars();
  2256. # endif
  2257.     }
  2258.     else
  2259.     {
  2260.     /* ":menutrans from to": add translation */
  2261.     from = arg;
  2262.     arg = menu_skip_part(arg);
  2263.     to = skipwhite(arg);
  2264.     *arg = NUL;
  2265.     arg = menu_skip_part(to);
  2266.     if (arg == to)
  2267.         EMSG(_(e_invarg));
  2268.     else
  2269.     {
  2270.         if (ga_grow(&menutrans_ga, 1) == OK)
  2271.         {
  2272.         tp = (menutrans_T *)menutrans_ga.ga_data;
  2273.         from = vim_strsave(from);
  2274.         to = vim_strnsave(to, (int)(arg - to));
  2275.         if (from != NULL && to != NULL)
  2276.         {
  2277.             tp[menutrans_ga.ga_len].from = from;
  2278.             tp[menutrans_ga.ga_len].to = to;
  2279.             ++menutrans_ga.ga_len;
  2280.             --menutrans_ga.ga_room;
  2281.         }
  2282.         }
  2283.     }
  2284.     }
  2285. #endif
  2286. }
  2287.  
  2288. #if defined(FEAT_MULTI_LANG) || defined(FEAT_TOOLBAR)
  2289. /*
  2290.  * Find the character just after one part of a menu name.
  2291.  */
  2292.     static char_u *
  2293. menu_skip_part(p)
  2294.     char_u    *p;
  2295. {
  2296.     while (*p != NUL && *p != '.' && !vim_iswhite(*p))
  2297.     {
  2298.     if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL)
  2299.         ++p;
  2300.     ++p;
  2301.     }
  2302.     return p;
  2303. }
  2304. #endif
  2305.  
  2306. #ifdef FEAT_MULTI_LANG
  2307. /*
  2308.  * Lookup part of a menu name in the translations.
  2309.  * Return a pointer to the translation or NULL if not found.
  2310.  */
  2311.     static char_u *
  2312. menutrans_lookup(name, len)
  2313.     char_u    *name;
  2314.     int        len;
  2315. {
  2316.     menutrans_T        *tp = (menutrans_T *)menutrans_ga.ga_data;
  2317.     int            i;
  2318.  
  2319.     for (i = 0; i < menutrans_ga.ga_len; ++i)
  2320.     if (STRNCMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL)
  2321.         return tp[i].to;
  2322.     return NULL;
  2323. }
  2324. #endif /* FEAT_MULTI_LANG */
  2325.  
  2326. #endif /* FEAT_MENU */
  2327.